package service

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strings"
	"time"

	"gitee.com/huangxinbo/xcas/internal/app/base"
	"gitee.com/huangxinbo/xcas/internal/dao"
	"gitee.com/huangxinbo/xcas/internal/model"
	"gitee.com/huangxinbo/xcas/internal/model/db_model"
	"github.com/gin-contrib/location"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

type OAuthService struct {
	base    *base.Base
	dao     *dao.DAO
	service *Service
}

func NewOAuthService(base *base.Base, dao *dao.DAO, service *Service) *OAuthService {
	return &OAuthService{
		base:    base,
		dao:     dao,
		service: service,
	}
}

// GetOAuthConfig 获取 OAuth 配置
func (s *OAuthService) GetOAuthConfig(ctx *gin.Context, provider model.OAuthProvider) (*model.OAuthConfig, error) {
	url := location.Get(ctx)
	if url == nil {
		return nil, errors.New("failed to get request URL")
	}

	baseURL := url.Scheme + "://" + url.Host

	switch provider {
	case model.OAuthProviderGitHub:
		return &model.OAuthConfig{
			ClientID:     s.base.Config.OAuth.GitHub.ClientID,
			ClientSecret: s.base.Config.OAuth.GitHub.ClientSecret,
			RedirectURI:  baseURL + "/api/oauth/callback/github",
			AuthURL:      "https://github.com/login/oauth/authorize",
			TokenURL:     "https://github.com/login/oauth/access_token",
			UserInfoURL:  "https://api.github.com/user",
		}, nil
	case model.OAuthProviderGitee:
		return &model.OAuthConfig{
			ClientID:     s.base.Config.OAuth.Gitee.ClientID,
			ClientSecret: s.base.Config.OAuth.Gitee.ClientSecret,
			RedirectURI:  baseURL + "/api/oauth/callback/gitee",
			AuthURL:      "https://gitee.com/oauth/authorize",
			TokenURL:     "https://gitee.com/oauth/token",
			UserInfoURL:  "https://gitee.com/api/v5/user",
		}, nil
	default:
		return nil, errors.New("unsupported OAuth provider")
	}
}

// GenerateAuthURL 生成 OAuth 认证 URL
func (s *OAuthService) GenerateAuthURL(ctx *gin.Context, provider model.OAuthProvider, state string) (string, error) {
	config, err := s.GetOAuthConfig(ctx, provider)
	if err != nil {
		return "", err
	}

	// 检查配置是否完整
	if config.ClientID == "" || config.ClientSecret == "" {
		return "", fmt.Errorf("OAuth configuration for %s is incomplete", provider)
	}

	params := url.Values{}
	params.Add("client_id", config.ClientID)
	params.Add("redirect_uri", config.RedirectURI)
	params.Add("response_type", "code")
	params.Add("state", state)
	params.Add("scope", s.getScope(provider))

	return config.AuthURL + "?" + params.Encode(), nil
}

// getScope 获取 OAuth 权限范围
func (s *OAuthService) getScope(provider model.OAuthProvider) string {
	switch provider {
	case model.OAuthProviderGitHub:
		return "user:email"
	case model.OAuthProviderGitee:
		return "user_info"
	default:
		return ""
	}
}

// ExchangeCodeForToken 使用授权码交换访问令牌
func (s *OAuthService) ExchangeCodeForToken(ctx *gin.Context, provider model.OAuthProvider, code string) (string, error) {
	config, err := s.GetOAuthConfig(ctx, provider)
	if err != nil {
		return "", err
	}

	params := url.Values{}
	params.Add("client_id", config.ClientID)
	params.Add("client_secret", config.ClientSecret)
	params.Add("code", code)
	params.Add("grant_type", "authorization_code")
	params.Add("redirect_uri", config.RedirectURI)

	req, err := http.NewRequest("POST", config.TokenURL, strings.NewReader(params.Encode()))
	if err != nil {
		return "", err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Set("Accept", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	var tokenResp struct {
		AccessToken string `json:"access_token"`
		TokenType   string `json:"token_type"`
		Scope       string `json:"scope"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
		return "", err
	}

	if tokenResp.AccessToken == "" {
		return "", errors.New("failed to get access token")
	}

	return tokenResp.AccessToken, nil
}

// GetUserInfo 获取用户信息
func (s *OAuthService) GetUserInfo(ctx *gin.Context, provider model.OAuthProvider, accessToken string) (*model.OAuthUserInfo, error) {
	config, err := s.GetOAuthConfig(ctx, provider)
	if err != nil {
		return nil, err
	}

	req, err := http.NewRequest("GET", config.UserInfoURL, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Accept", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	var userInfo model.OAuthUserInfo
	userInfo.Provider = provider
	userInfo.AccessToken = accessToken
	userInfo.ExpiresAt = time.Now().Add(24 * time.Hour) // 默认24小时过期

	switch provider {
	case model.OAuthProviderGitHub:
		var githubUser struct {
			ID        int    `json:"id"`
			Login     string `json:"login"`
			Name      string `json:"name"`
			Email     string `json:"email"`
			AvatarURL string `json:"avatar_url"`
		}
		if err := json.NewDecoder(resp.Body).Decode(&githubUser); err != nil {
			return nil, err
		}
		userInfo.ProviderID = fmt.Sprintf("%d", githubUser.ID)
		userInfo.Name = githubUser.Name
		if userInfo.Name == "" {
			userInfo.Name = githubUser.Login
		}
		userInfo.Email = githubUser.Email
		userInfo.AvatarURL = githubUser.AvatarURL

		// 如果 GitHub 没有返回邮箱，尝试获取用户邮箱
		if userInfo.Email == "" {
			userInfo.Email, err = s.getGitHubEmail(ctx, accessToken)
			if err != nil {
				s.base.Logger.Warn("Failed to get GitHub user email", zap.Error(err))
			}
		}

	case model.OAuthProviderGitee:
		var giteeUser struct {
			ID        int    `json:"id"`
			Name      string `json:"name"`
			Login     string `json:"login"`
			Email     string `json:"email"`
			AvatarURL string `json:"avatar_url"`
		}
		if err := json.NewDecoder(resp.Body).Decode(&giteeUser); err != nil {
			return nil, err
		}
		userInfo.ProviderID = fmt.Sprintf("%d", giteeUser.ID)
		userInfo.Name = giteeUser.Name
		if userInfo.Name == "" {
			userInfo.Name = giteeUser.Login
		}
		userInfo.Email = giteeUser.Email
		userInfo.AvatarURL = giteeUser.AvatarURL
	}

	return &userInfo, nil
}

// getGitHubEmail 获取 GitHub 用户邮箱
func (s *OAuthService) getGitHubEmail(ctx *gin.Context, accessToken string) (string, error) {
	req, err := http.NewRequest("GET", "https://api.github.com/user/emails", nil)
	if err != nil {
		return "", err
	}

	req.Header.Set("Authorization", "Bearer "+accessToken)
	req.Header.Set("Accept", "application/json")

	client := &http.Client{Timeout: 30 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	var emails []struct {
		Email    string `json:"email"`
		Primary  bool   `json:"primary"`
		Verified bool   `json:"verified"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&emails); err != nil {
		return "", err
	}

	for _, email := range emails {
		if email.Primary && email.Verified {
			return email.Email, nil
		}
	}

	for _, email := range emails {
		if email.Verified {
			return email.Email, nil
		}
	}

	if len(emails) > 0 {
		return emails[0].Email, nil
	}

	return "", errors.New("no email found")
}

// FindOrCreateUser 查找或创建用户
func (s *OAuthService) FindOrCreateUser(ctx *gin.Context, oauthUser *model.OAuthUserInfo) (*db_model.User, error) {
	// 1. 首先通过 OAuth 关联查找用户
	oauthUserRecord, err := s.dao.OAuthUserDAO.GetByProviderAndID(ctx, oauthUser.Provider, oauthUser.ProviderID)
	if err != nil {
		return nil, err
	}

	if oauthUserRecord != nil {
		// 找到关联的用户
		user, err := s.service.UserService.GetUser(ctx, oauthUserRecord.UserID)
		if err != nil {
			return nil, err
		}
		if user != nil {
			// 更新 OAuth 用户信息
			oauthUserRecord.AccessToken = oauthUser.AccessToken
			oauthUserRecord.ExpiresAt = oauthUser.ExpiresAt
			oauthUserRecord.UpdatedAt = &[]time.Time{time.Now()}[0]
			if err := s.dao.OAuthUserDAO.Update(ctx, oauthUserRecord); err != nil {
				s.base.Logger.Warn("Failed to update OAuth user", zap.Error(err))
			}
			return user, nil
		}
	}

	// 2. 通过邮箱查找用户
	user, err := s.service.UserService.GetUserByEmail(ctx, oauthUser.Email)
	if err != nil {
		return nil, err
	}

	if user != nil {
		// 用户存在，创建 OAuth 关联
		oauthUserRecord = &db_model.OAuthUser{
			UserID:       user.ID,
			Provider:     string(oauthUser.Provider),
			ProviderID:   oauthUser.ProviderID,
			Email:        oauthUser.Email,
			Name:         oauthUser.Name,
			AvatarURL:    &oauthUser.AvatarURL,
			AccessToken:  oauthUser.AccessToken,
			RefreshToken: &oauthUser.RefreshToken,
			ExpiresAt:    oauthUser.ExpiresAt,
		}
		if err := s.dao.OAuthUserDAO.Create(ctx, oauthUserRecord); err != nil {
			return nil, err
		}
		return user, nil
	}

	// 3. 创建新用户
	userCreateReq := &model.UserCreateRequest{
		Name:     oauthUser.Name,
		Email:    oauthUser.Email,
		Password: "", // OAuth 用户不需要密码
	}

	user, err = s.service.UserService.CreateUser(ctx, userCreateReq)
	if err != nil {
		return nil, err
	}

	// 设置用户为已验证状态
	now := time.Now()
	user.VerifiedAt = &now

	// 如果是新创建用户，写入到用户的 image 字段
	user.Image = &oauthUser.AvatarURL

	if err := s.dao.UserDAO.Update(ctx, user); err != nil {
		s.base.Logger.Warn("Failed to update user verification status", zap.Error(err))
	}

	// 创建 OAuth 关联
	oauthUserRecord = &db_model.OAuthUser{
		UserID:       user.ID,
		Provider:     string(oauthUser.Provider),
		ProviderID:   oauthUser.ProviderID,
		Email:        oauthUser.Email,
		Name:         oauthUser.Name,
		AvatarURL:    &oauthUser.AvatarURL,
		AccessToken:  oauthUser.AccessToken,
		RefreshToken: &oauthUser.RefreshToken,
		ExpiresAt:    oauthUser.ExpiresAt,
	}
	if err := s.dao.OAuthUserDAO.Create(ctx, oauthUserRecord); err != nil {
		return nil, err
	}

	s.base.Logger.Info("OAuth user created successfully",
		zap.String("provider", string(oauthUser.Provider)),
		zap.String("provider_id", oauthUser.ProviderID),
		zap.String("user_id", user.ID))

	return user, nil
}

// GetUserOAuthProviders 获取用户的 OAuth 提供商列表
func (s *OAuthService) GetUserOAuthProviders(ctx *gin.Context, userID string) ([]model.OAuthProviderInfo, error) {
	oauthUsers, err := s.dao.OAuthUserDAO.GetByUserID(ctx, userID)
	if err != nil {
		return nil, err
	}

	providers := []model.OAuthProviderInfo{
		{
			Provider: model.OAuthProviderGitHub,
			Name:     "GitHub",
			Enabled:  s.base.Config.OAuth.GitHub.ClientID != "" && s.base.Config.OAuth.GitHub.ClientSecret != "",
			Linked:   false,
		},
		{
			Provider: model.OAuthProviderGitee,
			Name:     "Gitee",
			Enabled:  s.base.Config.OAuth.Gitee.ClientID != "" && s.base.Config.OAuth.Gitee.ClientSecret != "",
			Linked:   false,
		},
	}

	// 标记已关联的提供商并设置 email 和 user_name
	for _, oauthUser := range oauthUsers {
		for i := range providers {
			if providers[i].Provider == model.OAuthProvider(oauthUser.Provider) {
				providers[i].Linked = true
				providers[i].Email = oauthUser.Email
				providers[i].UserName = oauthUser.Name
				break
			}
		}
	}

	return providers, nil
}

// LinkOAuthToCurrentUser 将 OAuth 提供商绑定到当前用户
func (s *OAuthService) LinkOAuthToCurrentUser(ctx *gin.Context, currentUserID string, provider model.OAuthProvider, oauthUser *model.OAuthUserInfo) error {
	// 检查 oauthUser 是否为 nil
	if oauthUser == nil {
		return fmt.Errorf("OAuth user info is nil")
	}

	// 检查必要的字段
	if oauthUser.ProviderID == "" {
		return fmt.Errorf("OAuth provider ID is empty")
	}

	// 检查该 OAuth 账号是否已被其他用户绑定
	existingOAuthUser, err := s.dao.OAuthUserDAO.GetByProviderAndID(ctx, provider, oauthUser.ProviderID)
	if err != nil {
		return err
	}

	if existingOAuthUser != nil && existingOAuthUser.UserID != currentUserID {
		return fmt.Errorf("OAuth account already linked to another user")
	}

	// 检查用户是否已经绑定了同一个提供商
	userOAuthProviders, err := s.dao.OAuthUserDAO.GetByUserID(ctx, currentUserID)
	if err != nil {
		return err
	}

	for _, userProvider := range userOAuthProviders {
		if userProvider.Provider == string(provider) {
			return fmt.Errorf("user has already linked this OAuth provider")
		}
	}

	// 创建 OAuth 关联
	oauthUserRecord := &db_model.OAuthUser{
		UserID:      currentUserID,
		Provider:    string(provider),
		ProviderID:  oauthUser.ProviderID,
		Email:       oauthUser.Email,
		Name:        oauthUser.Name,
		AccessToken: oauthUser.AccessToken,
		ExpiresAt:   oauthUser.ExpiresAt,
	}

	// 只有当 AvatarURL 不为空时才设置
	if oauthUser.AvatarURL != "" {
		oauthUserRecord.AvatarURL = &oauthUser.AvatarURL
	}

	if existingOAuthUser != nil {
		// 更新现有记录
		oauthUserRecord.ID = existingOAuthUser.ID
		if err := s.dao.OAuthUserDAO.Update(ctx, oauthUserRecord); err != nil {
			return err
		}
	} else {
		// 创建新记录
		if err := s.dao.OAuthUserDAO.Create(ctx, oauthUserRecord); err != nil {
			return err
		}
	}

	s.base.Logger.Info("OAuth provider linked successfully",
		zap.String("provider", string(provider)),
		zap.String("user_id", currentUserID),
		zap.String("oauth_email", oauthUser.Email))

	return nil
}

// SetPrimaryEmail 设置 OAuth 邮箱为主邮箱
func (s *OAuthService) SetPrimaryEmail(ctx *gin.Context, userID string, provider model.OAuthProvider) error {
	// 获取用户的 OAuth 信息
	oauthUser, err := s.dao.OAuthUserDAO.GetByUserIDAndProvider(ctx, userID, string(provider))
	if err != nil {
		return err
	}

	if oauthUser == nil {
		return fmt.Errorf("OAuth provider not linked")
	}

	// 检查 OAuth 邮箱是否与用户当前邮箱不同
	user, err := s.service.UserService.GetUser(ctx, userID)
	if err != nil {
		return err
	}

	if user == nil {
		return fmt.Errorf("user not found")
	}

	// 如果 OAuth 邮箱与用户当前邮箱相同，不需要更新
	if oauthUser.Email == user.Email {
		return fmt.Errorf("OAuth email is already the primary email")
	}

	// 检查邮箱是否已被其他用户使用
	existingUser, err := s.service.UserService.GetUserByEmail(ctx, oauthUser.Email)
	if err != nil {
		return err
	}

	if existingUser != nil && existingUser.ID != userID {
		return fmt.Errorf("email is already used by another user")
	}

	// 更新用户邮箱
	user.Email = oauthUser.Email
	now := time.Now()
	user.UpdatedAt = &now

	// 如果用户之前未验证，现在标记为已验证
	if user.VerifiedAt == nil {
		user.VerifiedAt = &now
	}

	if err := s.dao.UserDAO.Update(ctx, user); err != nil {
		return err
	}

	s.base.Logger.Info("Primary email updated from OAuth",
		zap.String("user_id", userID),
		zap.String("provider", string(provider)),
		zap.String("old_email", user.Email),
		zap.String("new_email", oauthUser.Email))

	return nil
}

// UnlinkOAuthProvider 解绑 OAuth 提供商
func (s *OAuthService) UnlinkOAuthProvider(ctx *gin.Context, userID string, provider model.OAuthProvider) error {
	return s.dao.OAuthUserDAO.DeleteByUserIDAndProvider(ctx, userID, string(provider))
}
